/*******************************************************************************
 * Copyright (c) 2011 Jovansonlee B. Cesar.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Jovansonlee B. Cesar - initial API and implementation
 ******************************************************************************/
package com.ivanceras.server.services;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.ivanceras.server.db.DatabaseManager;
import com.ivanceras.server.util.CStringUtils;
import com.ivanceras.server.util.DataTypeUtils;
import com.ivanceras.shared.dao.DAO_Ad_column;
import com.ivanceras.shared.dao.DAO_Ad_field;
import com.ivanceras.shared.dao.DAO_Ad_reference;
import com.ivanceras.shared.dao.DAO_Ad_tab;
import com.ivanceras.shared.dao.DAO_Ad_table;
import com.ivanceras.shared.dao.DAO_Ad_window;
import com.ivanceras.shared.db.DAO;
import com.ivanceras.shared.db.EMF;
import com.ivanceras.shared.db.EntityManager;
import com.ivanceras.shared.db.Filter;
import com.ivanceras.shared.db.ModelDef;
import com.ivanceras.shared.db.Order;
import com.ivanceras.shared.exception.DatabaseException;
import com.ivanceras.shared.exception.ModelDefinitionNotFoundException;
import com.ivanceras.shared.meta.ModelDefinitionProvider;
import com.ivanceras.shared.util.Configuration;

public class DataAPI {

	public static DAO_Ad_window[] retrieveWindowList(EntityManager em) throws DatabaseException{
		DAO[] daoList = em.getAll(new DAO_Ad_window(), new Order("name", false), new Filter("name",Filter.LIKE, "%"));
		DAO_Ad_window[] daoWindowList = DAO_Ad_window.cast(daoList);
		return daoWindowList;
	}
	public static DAO_Ad_tab[] retriveTabList(EntityManager em, DAO_Ad_window daoWindow) throws DatabaseException {
		DAO_Ad_tab[] tabList = null;
		tabList = DAO_Ad_tab.cast(em.getChildren(daoWindow, new DAO_Ad_tab(), new Order("seqno")));
		for(DAO_Ad_tab tab : tabList){
			DAO_Ad_table table = DAO_Ad_table.cast(em.getOne(new DAO_Ad_table(), 
					new Filter("ad_table_id", Filter.EQUAL, tab.getAd_table_id())));
			tab.setAd_table(table);
			DAO_Ad_field[] fieldList = DAO_Ad_field.cast(em.getAll(new DAO_Ad_field(), new Order("seqno"), 
					new Filter("ad_tab_id", Filter.EQUAL, tab.getAd_tab_id())
					));
			for(DAO_Ad_field field : fieldList){
				DAO_Ad_column column = DAO_Ad_column.cast(em.getOne(new DAO_Ad_column(), new Filter("ad_column_id", Filter.EQUAL, field.getAd_column_id())));
				if( column!=null ){
					DAO_Ad_reference reference = DAO_Ad_reference.cast(em.getOne(new DAO_Ad_reference(), new Filter("ad_reference_id", Filter.EQUAL, column.getAd_reference_id())));
					column.setAd_reference(reference);
					field.setAd_column(column);
				}
			}
			tab.setAd_fieldList(fieldList);
		}
		return tabList;
	}

	public static DAO[] retrieveChildrenTableData(EntityManager em, DAO_Ad_table parent, DAO_Ad_table table, Integer page, 
			Integer itemPerPage, Order order, Filter filter) throws DatabaseException {
		Order[] orders = null;
		if(order != null){
			orders = new Order[1];
			orders[0] = order;
		}

		Filter[] filters = null;
		if(filter != null){
			filters = new Filter[1];
			filters[0] = filter;
		}

		DAO[] daoList = em.getChildren(parent, table, orders, filters, page, itemPerPage, false);
		return daoList;
	}

	public static DAO[] retrieveTableData(EntityManager em, DAO_Ad_table table, Integer page, 
			Integer itemPerPage, Order order, Filter filter) throws DatabaseException {

		String tableName = CStringUtils.capitalize(table.getTablename().toLowerCase());
		Order[] orders = null;
		if(order != null){
			orders = new Order[1];
			orders[0] = order;
		}
		Filter[] filters = null;
		if(filter != null){
			
			filters = new Filter[1];
			filters[0] = filter;
		}

		DAO[] daoList = em.getAll(tableName, orders, filters, page, itemPerPage, false);
		return daoList;
	}
	public static Boolean saveDao(EntityManager em, DAO[] daoList) throws DatabaseException{
		if(Configuration.dbType.equals(DatabaseManager.BIGTABLE)){
			return saveDao(em, daoList, false);
		}else{
			return saveDao(em, daoList, true);
		}
	}
	
	public static Boolean saveDao(EntityManager em, DAO[] daoList, Boolean createTable) throws DatabaseException{
		for(DAO dao : daoList){
			if(createTable){
				em.createModel(em.getModelDefinition(dao.getModelName()));
			}
			em.insert(dao);
		}
		return true;
	}


	public static void importDAO(EntityManager em, String httpurl) throws MalformedURLException, DatabaseException {
		DAO[] daoList = parseXml(em, httpurl);
		saveDaoList(em, daoList);
	}
	
	private static DAO[] parseXml(EntityManager em, String httpurl) throws MalformedURLException{
		
		URL url = new URL(httpurl);

		List<DAO> daoList = new ArrayList<DAO>();
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder db = dbf.newDocumentBuilder();

			Document dom = db.parse(url.openStream());
			Element root = dom.getDocumentElement();
			NodeList models = dom.getElementsByTagName("model");
			int totalModel = models.getLength();

			for(int i = 0; i < totalModel; i++){
				Node nmodel = models.item(i);
				if(nmodel.getNodeType() == Node.ELEMENT_NODE){
					String table = ((Element)nmodel).getAttribute("name");
					NodeList rowList = ((Element)nmodel).getElementsByTagName("row");
					int totalRows = rowList.getLength();
					String modelName = CStringUtils.capitalize(table);
					DAO daoModel = new DAO(modelName);
					ModelDef modeldef = null;
					try {
						modeldef = em.getModelDefinition(daoModel.getModelName());
					} catch (DatabaseException e) {
						e.printStackTrace();
					}
					String[] model_attributes = modeldef.getAttributes();
					String[] model_datatypes = modeldef.getDataTypes();

					for(int r = 0; r < totalRows; r++){
						Node row = rowList.item(r);
						if(row.getNodeType() == Node.ELEMENT_NODE){
							NodeList attributes = ((Element)row).getChildNodes();
							DAO dao = new DAO(CStringUtils.capitalize(table));
							int totalAttributes = attributes.getLength();
							for(int a = 0; a < totalAttributes; a++){
								Node attrib = attributes.item(a);
								if(attrib.getNodeType() == Node.ELEMENT_NODE){
									Node nvalue = attrib.getFirstChild();
									if(nvalue.getNodeType() == Node.CDATA_SECTION_NODE){
										String attribute = attrib.getNodeName();
										String s_value = nvalue.getNodeValue();
										String dataType = model_datatypes[indexOf(model_attributes, attribute)]; 
										Object o_value = DataTypeUtils.convertFromString(s_value, dataType);
										dao.set_Value(attribute, o_value);
									}
								}
							}
							daoList.add(dao);
						}
					}
				}
			}

		}catch(ParserConfigurationException pce) {
			pce.printStackTrace();
		}catch(SAXException se) {
			se.printStackTrace();
		}catch(IOException ioe) {
			ioe.printStackTrace();
		}
		return daoList.toArray(new DAO[daoList.size()]);
	}
	
	
	private static int indexOf(String[] attributes, String key){
		for(int i = 0; i < attributes.length; i++){
			if(key.equals(attributes[i])){
				return i;
			}
		}
		return -1;
	}
	
	private static void saveDaoList(EntityManager em, DAO[] daoList) throws DatabaseException{
		for(int i = 0; i < daoList.length; i++){
			DAO dao = daoList[i];
			Object value = dao.get_Value("name");
			em.insert(dao);
		}
	}

	public static HashMap<String, Integer> parseTableListXml(String xmlurl) throws MalformedURLException{
		
		URL url = new URL(xmlurl);

		HashMap<String, Integer> tableCountList = new HashMap<String, Integer>();
		
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder db = dbf.newDocumentBuilder();

			Document dom = db.parse(url.openStream());
			Element root = dom.getDocumentElement();
			NodeList models = dom.getElementsByTagName("model");
			int totalModel = models.getLength();

			for(int i = 0; i < totalModel; i++){
				Node nmodel = models.item(i);
				if(nmodel.getNodeType() == Node.ELEMENT_NODE){
					String table = ((Element)nmodel).getAttribute("name");
					Integer count = Integer.parseInt(((Element)nmodel).getAttribute("count"));
					tableCountList.put(table, count);
				}
			}

		}catch(ParserConfigurationException pce) {
			pce.printStackTrace();
		}catch(SAXException se) {
			se.printStackTrace();
		}catch(IOException ioe) {
			ioe.printStackTrace();
		}
		return tableCountList;
	}
	
	public static HashMap<String, String> retrieveListData(EntityManager em, DAO_Ad_column daoColumn) throws DatabaseException{
		DAO_Ad_reference reference = daoColumn.getAd_reference();
		System.out.println("\tRetrieiving list data for column "+daoColumn.getName());
		String referenceName = reference.getName();
		HashMap<String, String> listData = new HashMap<String, String>();
		if(referenceName.equals("Table Direct")){
			
			//use the table refered to this column for the lookup;
			String table = getReferenceTable(em, daoColumn);
			table = CStringUtils.capitalize(table.toLowerCase());
			DAO daoTable = new DAO(table);
			System.out.println("\t\tRetrieiving list data from "+table);
			DAO[] daoList = em.getAll(daoTable);
			
			for(DAO dao : daoList){
				String id = dao.get_Value(table.toLowerCase()+"_id").toString();
				String value = dao.get_Value("name").toString();//TODO: use the Identifier columns for the columns to be displayed
				listData.put(id, value);
			}
			return listData;
		}else if(referenceName.equals("Table")){
			//use the column specified as the linker of the table;
			System.out.println("\t\tTable");
		}else if(referenceName.equals("List")){
			System.out.println("\t\tList");
			//use the reference lookup;
			DAO refList = new DAO("Ad_ref_list");
			DAO[] daoList = em.getAll(refList,new Order("name"),  new Filter("ad_reference_id", Filter.EQUAL, daoColumn.getAd_reference_value_id()));
			for(DAO dao : daoList){
				String value = dao.get_Value("name").toString();
				String identifer = dao.get_Value("value").toString();
				listData.put(identifer, value);
			}
			return listData;
		}
		
		return null;
		
	}
	
	public static String getReferenceTable(EntityManager em, DAO_Ad_column column) throws DatabaseException{
		DAO_Ad_table daoTable = DAO_Ad_table.cast(em.getOne(new DAO_Ad_table(), new Filter("ad_table_id", Filter.EQUAL, column.getAd_table_id())));
		String tableName = CStringUtils.capitalize(daoTable.getTablename().toLowerCase());
		try {
			ModelDef modelDef = ModelDefinitionProvider.getDefinition(tableName);
			String refedTable = modelDef.getReferencedTable(column.getColumnname());
			return refedTable;
		} catch (ModelDefinitionNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	public static void main(String[] args) throws DatabaseException{
		EntityManager em = EMF.get();
		DAO_Ad_window[] windows = DAO_Ad_window.cast(em.getAll(new DAO_Ad_window(), new Order("name", false), new Filter("name",Filter.LIKE, "%")));
		for(DAO_Ad_window win : windows){
			System.out.println(win.getName());
		}
	}
	public static String retrieveDataName(EntityManager em,
			DAO_Ad_column daoColumn, String value) throws DatabaseException {
		String table = CStringUtils.capitalize(daoColumn.getColumnname().toLowerCase().replaceAll("_id$", ""));
		DAO daoTable = new DAO(table);
		System.out.println("\t\tRetrieiving list data from "+table);
		DAO dao = em.getOne(daoTable, new Filter(table.toLowerCase()+"_id", Filter.EQUAL, value));
		String dataName = dao.get_Value("name").toString();
		return dataName;
	}
}
